﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TGJ_GGA;
using System.IO;

namespace bonden
{
    class Program
    {
        private static StreamWriter sw = null;
        private static double bestfitnesssofar = double.MaxValue;

        private static float[,] data = new float[10, 2];

        private static Random rnd = new Random();

        static void Main(string[] args)
        {
            GAInit gainit = new GAInit();
            gainit.numberOfGenerations = int.MaxValue;
            gainit.populationSize = 2500;
            gainit.geneSizeInBytes = 3*sizeof(Int32);
            gainit.elitsm = true;
            gainit.aimForHighFitness = false;
            gainit.keep = 25;
            gainit.allowClones = false;

            sw = new StreamWriter("bonde.dat", false);

            ByteArrayGA BAGA = new ByteArrayGA(gainit, Fitit, ForeachGeneration);

            BAGA.manipulatorHandler.add(BAGA.ByteCrossover, 0.85, "BitToggle");
            BAGA.manipulatorHandler.add(BAGA.ByteToggle, 0.015, "BitToggle");
            BAGA.manipulatorHandler.add(BAGA.Substitution, 0.035, "Substitution");
            BAGA.manipulatorHandler.add(BAGA.ByteSubSubstitution, 0.01, "BitSubSubstitution");
            BAGA.manipulatorHandler.add(BAGA.ByteSwap, 0.50, "swap");
            BAGA.manipulatorHandler.add(inc, 0.22, "dec first");
            BAGA.manipulatorHandler.add(dec, 0.22, "inc first");
            
            Gene result = BAGA.Run();
            sw.Flush();
            sw.Close();
        }

        //4 bytes
        public static Gene inc(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(Int32));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(Int32);
            int data = ByteArrayGA.ReadInt(ref i, tempGene.GeneData);
            data++;
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(Int32))] = tempdata[j];
            }
            return tempGene;
        }

        public static Gene dec(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(Int32));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(Int32);
            int data = ByteArrayGA.ReadInt(ref i, tempGene.GeneData);
            data--;
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(Int32))] = tempdata[j];
            }
            return tempGene;
        }
        //4 bytes
        public static Gene upPercent(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(byte));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(byte);
            byte data = ByteArrayGA.ReadByte(ref i, tempGene.GeneData);
            data = (byte)(data * 2);
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(byte))] = tempdata[j];
            }
            return tempGene;
        }

        public static Gene downPercent(Gene g)
        {
            int startIndex = rnd.Next(g.GeneData.Length / sizeof(byte));

            Gene tempGene = ByteArrayGA.CopyGene(g);
            int i = startIndex * sizeof(byte);
            byte data = ByteArrayGA.ReadByte(ref i, tempGene.GeneData);
            data = (byte)(data / 2);
            byte[] tempdata = BitConverter.GetBytes(data);
            for (byte j = 0; j < 1; j++)
            {
                tempGene.GeneData[j + startIndex * (sizeof(byte))] = tempdata[j];
            }
            return tempGene;
        }

        //return false to stop run.
        public static void Fitit(Gene g)
        {
            int i = 0;
            int x = ByteArrayGA.ReadInt(ref i, g.GeneData) % 100;
            int y = ByteArrayGA.ReadInt(ref i, g.GeneData) % 100;
            int z = ByteArrayGA.ReadInt(ref i, g.GeneData) % 100;

            g.fitness = 0;

            if (x < 1 || y < 1 || z < 1 || x+y+z !=100)
            {
                g.fitness = double.MaxValue;
                return;
            }
            double value = x * 10.0 + 3.0 * y + 0.5 * z;

            g.fitness = Math.Abs(100.0 - value);
        }

        public static void ForeachGeneration(int i, List<Gene> pop)
        {
            if (double.IsInfinity(pop[0].fitness))
            {
                Console.ReadKey();
            }
            int j = 0;
            int x = ByteArrayGA.ReadInt(ref j, pop[0].GeneData) % 100;
            int y = ByteArrayGA.ReadInt(ref j, pop[0].GeneData) % 100;
            int z = ByteArrayGA.ReadInt(ref j, pop[0].GeneData) % 100;


            if (pop[0].fitness < bestfitnesssofar)
            {
                bestfitnesssofar = pop[0].fitness;
                sw.WriteLine(i.ToString() +
                    "\t" + pop[0].fitness.ToString("0.000000") +
                    "\t" + x +
                    "\t" + y +
                    "\t" + z);
                sw.Flush();
                Console.WriteLine(i.ToString() + 
                    "\t" + pop[0].fitness.ToString("0.000000") +
                    "\t" + x +
                    "\t" + y +
                    "\t" + z);
            }
            if (pop[0].fitness == 0)
            {
                Console.WriteLine("DONE!!");
                Console.ReadKey();
                Environment.Exit(0);
            }
        }
    }
}
